
#region Imports

using System;
using System.Collections.Generic;
using System.Text;

using Justa.Reflection;

#endregion

namespace Justa.Aspect
{
    /// <summary>
    /// 
    /// </summary>
    /// 
    /// <author>Abel Perez</author>
    /// 
    public delegate void BeforeAdvice();

    /// <summary>
    /// 
    /// </summary>
    /// 
    /// <param name="returnValue"></param>
    /// 
    public delegate void AfterAdvice(object returnValue);

    /// <summary>
    /// 
    /// </summary>
    /// 
    /// <typeparam name="T"></typeparam>
    /// 
    /// <param name="returnValue"></param>
    /// 
    public delegate void AfterAdvice<T>(T returnValue);

    /// <summary>
    /// 
    /// </summary>
    /// 
    /// <param name="joinPoint"></param>
    /// 
    /// <returns></returns>
    /// 
    public delegate object AroundAdvice(JoinPoint joinPoint);

    /// <summary>
    /// 
    /// </summary>
    /// 
    /// <param name="exception"></param>
    /// 
    public delegate void AfterThrowsAdvice(Exception exception);

    /// <summary>
    /// 
    /// </summary>
    /// 
    public class AspectProxyFactory
    {
        /// <summary>
        /// 
        /// </summary>
        /// 
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="K"></typeparam>
        /// 
        /// <param name="beforeAdvice"></param>
        /// 
        /// <returns></returns>
        /// 
        public static T CreateProxy<T, K>(BeforeAdvice beforeAdvice)
        {
            object target = Activator.CreateInstance<K>();
            return CreateProxy<T>(target, beforeAdvice);
        }

        /// <summary>
        /// 
        /// </summary>
        /// 
        /// <typeparam name="T"></typeparam>
        /// 
        /// <param name="target"></param>
        /// <param name="beforeAdvice"></param>
        /// 
        /// <returns></returns>
        /// 
        public static T CreateProxy<T>(object target, BeforeAdvice beforeAdvice)
        {
            return CreateProxy<T>(target, beforeAdvice, null, null);
        }

        /// <summary>
        /// 
        /// </summary>
        /// 
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="K"></typeparam>
        /// 
        /// <param name="afterAdvice"></param>
        /// 
        /// <returns></returns>
        /// 
        public static T CreateProxy<T, K>(AfterAdvice afterAdvice)
        {
            object target = Activator.CreateInstance<K>();
            return CreateProxy<T>(target, afterAdvice);
        }

        /// <summary>
        /// 
        /// </summary>
        /// 
        /// <typeparam name="T"></typeparam>
        /// 
        /// <param name="target"></param>
        /// <param name="afterAdvice"></param>
        /// 
        /// <returns></returns>
        /// 
        public static T CreateProxy<T>(object target, AfterAdvice afterAdvice)
        {
            return CreateProxy<T>(target, null, afterAdvice, null);
        }

        /// <summary>
        /// 
        /// </summary>
        /// 
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="K"></typeparam>
        /// 
        /// <param name="beforeAdvice"></param>
        /// <param name="afterAdvice"></param>
        /// 
        /// <returns></returns>
        /// 
        public static T CreateProxy<T, K>(BeforeAdvice beforeAdvice, AfterAdvice afterAdvice)
        {
            object target = Activator.CreateInstance<K>();
            return CreateProxy<T>(target, beforeAdvice, afterAdvice, null);
        }

        /// <summary>
        /// 
        /// </summary>
        /// 
        /// <typeparam name="T"></typeparam>
        /// 
        /// <param name="target"></param>
        /// <param name="beforeAdvice"></param>
        /// <param name="afterAdvice"></param>
        /// 
        /// <returns></returns>
        /// 
        public static T CreateProxy<T>(object target, BeforeAdvice beforeAdvice, AfterAdvice afterAdvice)
        {
            return CreateProxy<T>(target, beforeAdvice, afterAdvice, null);
        }

        /// <summary>
        /// 
        /// </summary>
        /// 
        /// <typeparam name="T"></typeparam>
        /// 
        /// <param name="target"></param>
        /// <param name="beforeAdvice"></param>
        /// <param name="afterAdvice"></param>
        /// 
        /// <returns></returns>
        /// 
        public static T CreateProxy<T>(object target, BeforeAdvice beforeAdvice, AfterAdvice afterAdvice, AfterThrowsAdvice throws)
        {
            AspectInvoker handler = new AspectInvoker(target, typeof(T));

            if (beforeAdvice != null)
            {
                handler.before += beforeAdvice;
            }
            if (afterAdvice != null)
            {
                handler.after += afterAdvice;
            }
            if (throws != null)
            {
                handler.throws += throws;
            }

            T result = (T)handler.GetTransparentProxy();
            return result;
        }

        /// <summary>
        /// 
        /// </summary>
        /// 
        /// <typeparam name="T"></typeparam>
        /// 
        /// <param name="target"></param>
        /// <param name="advice"></param>
        /// 
        /// <returns></returns>
        /// 
        public static T CreateProxy<T>(object target, AroundAdvice advice)
        {
            AspectInvoker handler = new AspectInvoker(target, typeof(T));
            handler.invoker += advice;

            T result = (T)handler.GetTransparentProxy();
            return result;
        }

        /// <summary>
        /// 
        /// </summary>
        /// 
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="T0"></typeparam>
        /// 
        /// <param name="target"></param>
        /// <param name="beforeAdvice"></param>
        /// <param name="afterAdvice"></param>
        /// 
        /// <returns></returns>
        /// 
        public static T CreateProxy<T>(object target, AfterThrowsAdvice afterThrowsAdvice)
        {
            AspectInvoker handler = new AspectInvoker(target, typeof(T));

            if (afterThrowsAdvice != null)
            {
                handler.throws += afterThrowsAdvice;
            }

            T result = (T)handler.GetTransparentProxy();
            return result;
        }
    }
}
